//
// Created by hamlet on 23-6-19.
//

#include "Sem.h"
#include <fmt/core.h>

#include <utility>

Sem::Sem(Lex &lex, Gram &gram) : idList(lex.idList), valueIntList(lex.valueIntList), root(gram.root) {}

void Sem::genNode(const std::shared_ptr<Token> &node) {
    static const auto addTmpVar = [this]() {
        return (++curTmpIndex) << 8;
    };
    static const auto checkIdType = [this](int idIndex, TokenType t) {
        if (idType[idIndex] == TokenType::Null) {
            throw ExcUndefinedVariable(idList[idIndex]);
        } else if (idType[idIndex] != t) {
            throw ExcTypeMismatch(t, idType[idIndex]);
        }
    };
    static const auto BACKPATCH = [this](int obj, int addr) {
        if (obj == -1) return;
        int nobj;
        while (quads[obj].result != -1) {
            nobj = quads[obj].result;
            quads[obj].result = addr;
            obj = nobj;
        }
        quads[obj].result = addr;
    };
    static const auto MERGE = [this](int a1, int a2) {
        if (a1 == -1) {
            return a2;
        } else if (a2 == -1) {
            return a1;
        }
        quads[a2].result = a1;
        return a2;
    };
    static const auto MERGE3 = [this](int a1, int a2, int a3) {
        if (a1 == -1) return MERGE(a2, a3);
        if (a2 == -1) return MERGE(a1, a3);
        if (a3 == -1) return MERGE(a1, a2);
        quads[a3].result = a2;
        quads[a2].result = a1;
        return 3;
    };
    const auto &ch = node->children;
    switch (node->type) {
        case TokenType::E: {
            if (ch.size() == 1) {
                genNode(ch[0]);
                node->attr["loc"] = ch[0]->attr["loc"];
            } else {
                genNode(ch[0]);
                genNode(ch[2]);
                int tx = addTmpVar();
                quads.emplace_back(ch[1]->type == TokenType::OpPlus ? QuadType::Plus : QuadType::Minus,
                                   ch[0]->attr["loc"], ch[2]->attr["loc"], tx);
                node->attr["loc"] = tx;
            }
        }
            break;
        case TokenType::T: {
            if (ch.size() == 1) {
                genNode(ch[0]);
                node->attr["loc"] = ch[0]->attr["loc"];
            } else {
                genNode(ch[0]);
                genNode(ch[2]);
                int tx = addTmpVar();
                quads.emplace_back(ch[1]->type == TokenType::OpMultiply ? QuadType::Multiply : QuadType::Divide,
                                   ch[0]->attr["loc"], ch[2]->attr["loc"], tx);
                node->attr["loc"] = tx;
            }
        }
            break;
        case TokenType::F: {
            if (ch[0]->type == TokenType::Id) {
                checkIdType(ch[0]->value, TokenType::Integer);
                node->attr["loc"] = ch[0]->value;
            } else if (ch[0]->type == TokenType::ValueInt) {
                node->attr["loc"] = (ch[0]->value + 1) << 4;
            } else {
                genNode(ch[1]);
                node->attr["loc"] = ch[1]->attr["loc"];
            }
        }
            break;
        case TokenType::E1: {
            if (ch.size() == 1) {
                int NXQ = static_cast<int>(quads.size());
                node->attr["TC"] = NXQ;
                node->attr["FC"] = NXQ + 1;
                TokenType t = ch[0]->type;
                switch (t) {
                    case TokenType::Id: {
                        checkIdType(ch[0]->value, TokenType::Boolean);
                        quads.emplace_back(QuadType::JNZ, ch[0]->value, -1, -1);
                    }
                        break;
                    case TokenType::True:
                        quads.emplace_back(QuadType::J, -1, -1, -1);
                        break;
                    case TokenType::False:
                        quads.emplace_back(QuadType::JNZ, -1, -1, -1);
                        break;
                    default: {
                    }
                }
                quads.emplace_back(QuadType::J, -1, -1, -1);
            } else {
                genNode(ch[0]);
                genNode(ch[2]);
                int NXQ = static_cast<int>(quads.size());
                node->attr["TC"] = NXQ;
                node->attr["FC"] = NXQ + 1;
                QuadType qt;
                TokenType tt = ch[1]->type;
                switch (tt) {
                    case TokenType::OpGreater:
                        qt = QuadType::JGreater;
                        break;
                    case TokenType::OpLess:
                        qt = QuadType::JLess;
                        break;
                    case TokenType::OpEq:
                        qt = QuadType::JEq;
                        break;
                    default: {
                    }
                }
                quads.emplace_back(qt, ch[0]->attr["loc"], ch[2]->attr["loc"], -1);
                quads.emplace_back(QuadType::J, -1, -1, -1);
            }
        }
            break;
        case TokenType::E2: {
            if (ch.size() == 4) {
                if (ch[1]->type == TokenType::OpAnd) {
                    genNode(ch[0]);
                    ch[2]->attr["quad"] = static_cast<int>(quads.size());
                    genNode(ch[3]);
                    BACKPATCH(ch[0]->attr["TC"], ch[2]->attr["quad"]);
                    node->attr["TC"] = ch[3]->attr["TC"];
                    node->attr["FC"] = MERGE(ch[0]->attr["FC"], ch[3]->attr["FC"]);
                } else {
                    genNode(ch[0]);
                    ch[2]->attr["quad"] = static_cast<int>(quads.size());
                    genNode(ch[3]);
                    BACKPATCH(ch[0]->attr["FC"], ch[2]->attr["quad"]);
                    node->attr["TC"] = MERGE(ch[0]->attr["TC"], ch[3]->attr["TC"]);
                    node->attr["FC"] = ch[3]->attr["FC"];
                }
            } else if (ch.size() == 1) {
                genNode(ch[0]);
                node->attr["TC"] = ch[0]->attr["TC"];
                node->attr["FC"] = ch[0]->attr["FC"];
            } else {
                // TODO 这里文法设计有问题
                genNode(ch[1]);
                node->attr["TC"] = ch[1]->attr["FC"];
                node->attr["FC"] = ch[1]->attr["TC"];
            }
        }
            break;
        case TokenType::T2: {
            if (ch.size() == 1) {
                genNode(ch[0]);
                node->attr["TC"] = ch[0]->attr["TC"];
                node->attr["FC"] = ch[0]->attr["FC"];
            } else {
                genNode(ch[1]);
                node->attr["TC"] = ch[1]->attr["TC"];
                node->attr["FC"] = ch[1]->attr["FC"];
            }
        }
            break;
        case TokenType::S1: {
            if (ch.empty()) {
                node->attr["nl"] = -1;
            } else {
                genNode(ch[0]);
                ch[1]->attr["quad"] = static_cast<int>(quads.size());
                genNode(ch[2]);
                BACKPATCH(ch[0]->attr["nl"], ch[1]->attr["quad"]);
                node->attr["nl"] = ch[2]->attr["nl"];
            }
        }
            break;
        case TokenType::S2: {
            genNode(ch[1]);
            node->attr["nl"] = ch[1]->attr["nl"];
        }
            break;
        case TokenType::S: {
            switch (ch[0]->type) {
                case TokenType::OpSep: {
                    node->attr["nl"] = -1;
                }
                    break;
                case TokenType::Var: {
                    TokenType tp = ch[ch.size() - 2]->children[0]->type;
                    for (int i = 1; i < (ch.size() - 3); i += 2) {
                        idType[ch[i]->value] = tp;
                    }
                    node->attr["nl"] = -1;
                }
                    break;
                case TokenType::Id: {
                    if (ch[2]->type == TokenType::E) {
                        checkIdType(ch[0]->value, TokenType::Integer);
                    } else {
                        checkIdType(ch[0]->value, TokenType::Boolean);
                    }
                    genNode(ch[2]);
                    quads.emplace_back(QuadType::Assign, ch[2]->attr["loc"], -1, ch[0]->value);
                    node->attr["nl"] = -1;
                }
                    break;
                case TokenType::If: {
                    genNode(ch[1]);
                    ch[3]->attr["quad"] = static_cast<int>(quads.size());
                    genNode(ch[4]);
                    BACKPATCH(ch[1]->attr["TC"], ch[3]->attr["quad"]);
                    if (ch.size() == 6) {
                        node->attr["nl"] = MERGE(ch[1]->attr["FC"], ch[4]->attr["nl"]);
                    } else {
                        ch[5]->attr["nl"] = static_cast<int>(quads.size());
                        quads.emplace_back(QuadType::J, -1, -1, -1);
                        ch[7]->attr["quad"] = static_cast<int>(quads.size());
                        genNode(ch[8]);
                        BACKPATCH(ch[1]->attr["FC"], ch[7]->attr["quad"]);
                        node->attr["nl"] = MERGE3(ch[4]->attr["nl"], ch[5]->attr["nl"], ch[8]->attr["nl"]);
                    }
                }
                    break;
                case TokenType::While: {
                    ch[1]->attr["quad"] = static_cast<int>(quads.size());
                    genNode(ch[2]);
                    ch[4]->attr["quad"] = static_cast<int>(quads.size());
                    genNode(ch[5]);
                    BACKPATCH(ch[2]->attr["TC"], ch[4]->attr["quad"]);
                    BACKPATCH(ch[5]->attr["nl"], ch[1]->attr["quad"]);
                    node->attr["nl"] = ch[2]->attr["FC"];
                    quads.emplace_back(QuadType::J, -1, -1, ch[1]->attr["quad"]);
                }
                    break;
                case TokenType::Print: {
                    genNode(ch[2]);
                    quads.emplace_back(QuadType::Print, ch[2]->attr["loc"], -1, -1);
                    node->attr["nl"] = -1;
                }
                    break;
                default: {
                }
            }
        }
            break;
        case TokenType::P: {
            quads.emplace_back(QuadType::Start, 0, -1, -1);
            genNode(ch[3]);
            quads.emplace_back(QuadType::Exit, 1 << 4, -1, -1);
        }
            break;
        default: {
        }
    }
}

bool Sem::generate() {
    quads.clear();
    curTmpIndex = 0;
    idType.clear();
    idType.resize(idList.size(), TokenType::Null);
    Sem::simplify(root);
    try {
        genNode(root);
    } catch (ExcSem &e) {
        fmt::print("语义错误：{}\n", e.what());
        return false;
    }
    return true;
}

void Sem::output() const {
    static const auto show = [this](int i) -> std::string {
        if (i < 0) {
            return "_";
        } else if (i < (1 << 4)) {
            return idList[i];
        } else if (i < (1 << 8)) {
            return std::to_string(valueIntList[(i >> 4) - 1]);
        } else {
            return "T" + std::to_string(i >> 8);
        }
    };
    fmt::print("变量表：\n");
    for (int i = 0; i < idList.size(); ++i) {
        fmt::print("{} {} {}\n", i, idList[i], Gram::getTokenName(idType[i]));
    }
    fmt::print("常数表：\n");
    for (int i = 0; i < valueIntList.size(); ++i) {
        fmt::print("{} {}\n", i, valueIntList[i]);
    }
    fmt::print("四元式序列：\n");
    for (int i = 0; i < quads.size(); ++i) {
        auto name = quadName.find(quads[i].op)->second;
        fmt::print("{} {} {} {} {}\n", i + 100, name, show(quads[i].arg1), show(quads[i].arg2),
                   name[0] == 'j' ? std::to_string(quads[i].result + 100) : show(quads[i].result));
    }
}

void Sem::simplify(std::shared_ptr<Token> node) {
    auto del_node = [&node](int index) {
        std::shared_ptr<Token> parent = node->parent.lock();
        auto bg = parent->children.begin();
        if (index < 0) {
            index += static_cast<int>(parent->children.size());
        }
        parent->children.erase(bg + index);
        parent->children.insert(bg + index, node->children.begin(), node->children.end());
        for (const auto &nd: node->children) {
            nd->parent = parent;
        }
        node = parent->children[index];
    };
    switch (node->type) {
        case TokenType::S2_: {
            del_node(5);
        }
            break;
        case TokenType::E_:
        case TokenType::T_:
        case TokenType::E2_: {
            del_node(-1);
        }
            break;
        case TokenType::E1_: {
            del_node(1);
        }
            break;
        case TokenType::IdList:
        case TokenType::IdList_: {
            del_node(-4);
        }
            break;
        case TokenType::Printable: {
            del_node(2);
        }
            break;
        default: {
        }
    }
    for (int i = 0; i < node->children.size(); ++i) {
        simplify(node->children[i]);
    }
}

const std::unordered_map<QuadType, std::string> Sem::quadName{
        {QuadType::Plus,     "+"},
        {QuadType::Minus,    "-"},
        {QuadType::Multiply, "*"},
        {QuadType::Divide,   "/"},
        {QuadType::Assign,   ":="},
        {QuadType::J,        "j"},
        {QuadType::JEq,      "j="},
        {QuadType::JGreater, "j>"},
        {QuadType::JLess,    "j<"},
        {QuadType::JNZ,      "jnz"},
        {QuadType::Print,    "print"},
        {QuadType::Start,    "start"},
        {QuadType::Exit,     "exit"},
};

Sem::ExcSem::ExcSem(std::string desc) : desc_(std::move(desc)) {}

const char *Sem::ExcSem::what() const noexcept {
    return desc_.c_str();
}

Sem::ExcUndefinedVariable::ExcUndefinedVariable(const std::string &idName)
        : ExcSem(fmt::format("变量{}未定义", idName)) {}

Sem::ExcTypeMismatch::ExcTypeMismatch(TokenType expected, TokenType actual)
        : ExcSem(
        fmt::format("类型不匹配，期望为{}，实际为{}", Gram::getTokenName(expected), Gram::getTokenName(actual))) {}
